#include "ONB.h"

#define ONB_EPSILON	0.01f

ONB::ONB()
{

}

ONB::ONB(const Vector3& a, const Vector3& b, const Vector3& c)
{
	U = a;
	V = b;
	W = c;
}

void ONB::initFromU(const Vector3& u)
{
	Vector3 n(1.0f, 0.0f, 0.0f);
	Vector3 m(0.0f, 1.0f, 0.0f);

	U = unitVector(u);
	V = cross(U, n);
	if (V.squaredLength() < ONB_EPSILON)
		V = cross(U, m);
	W = cross(U, V);
}

void ONB::initFromV(const Vector3& v)
{
	Vector3 n(1.0f, 0.0f, 0.0f);
	Vector3 m(0.0f, 1.0f, 0.0f);

	V= unitVector(v);
	U = cross(V, n);
	if (U.squaredLength() < ONB_EPSILON)
		U = cross(V, m);
	W = cross(U, V);
}

void ONB::initFromW(const Vector3& w)
{
	Vector3 n(1.0f, 0.0f, 0.0f);
	Vector3 m(0.0f, 1.0f, 0.0f);

	W = unitVector(w);
	U = cross(W, n);
	if (U.squaredLength() < ONB_EPSILON)
		U = cross(W, m);
	V = cross(W, U);
}

void ONB::set(const Vector3& a, const Vector3& b, const Vector3& c)
{
	U = a;
	V = b;
	W = c;
}

void ONB::initFromUV(const Vector3& u, const Vector3& v)
{
	U = unitVector(u);
	W = unitVector(cross(u, v));
	V = cross(W, U);
}

void ONB::initFromVU(const Vector3& v, const Vector3& u)
{
	V = unitVector(v);
	W = unitVector(cross(u, v));
	U = cross(V, W);
}

void ONB::initFromUW(const Vector3& u, const Vector3& w)
{
	U = unitVector(u);
	V = unitVector(cross(w, u));
	W = cross(U, V);
}

void ONB::initFromWU(const Vector3& w, const Vector3& u)
{
	W = unitVector(w);
	V = unitVector(cross(w, u));
	U = cross(V, W);
}

void ONB::initFromVW(const Vector3& v, const Vector3& w)
{
	V = unitVector(v);
	U = unitVector(cross(v, w));
	W = cross(U, V);
}

void ONB::initFromWV(const Vector3& w, const Vector3& v)
{
	W = unitVector(w);
	U = unitVector(cross(v, w));
	V = cross(W, U);
}

istream& operator>>(istream& is, ONB& t)
{
	Vector3 u, v, w;
	is >> u >> v >> w;
	t.initFromUV(u, v);
	return is;
}

ostream& operator<<(ostream& os, const ONB &t)
{
	os << t.U << '\n' << t.V << '\n' << t.W << '\n';
	return os;
}

bool operator==(const ONB &o1, const ONB &o2)
{
	return o1.U == o2.U
		&& o1.V == o2.V
		&& o1.W == o2.W;
}

Vector3 ONB::u() const
{
	return U;
}

Vector3 ONB::v() const
{
	return V;
}

Vector3 ONB::w() const
{
	return W;
}